{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象(OOP)基本概念\n",
    "\n",
    "**面向对象编程** —— `Object Oriented Programming` 简写 `OOP`\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 了解 **面向对象** 基本概念\n",
    "\n",
    "## 01. 面向对象基本概念\n",
    "\n",
    "* 我们之前学习的编程方式就是 **面向过程** 的\n",
    "* **面相过程** 和 **面相对象**，是两种不同的 **编程方式**\n",
    "* 对比 **面向过程** 的特点，可以更好地了解什么是 **面向对象**\n",
    "\n",
    "### 1.1 过程和函数（科普）\n",
    "\n",
    "* **过程** 是早期的一个编程概念\n",
    "* **过程** 类似于函数，只能执行，但是没有返回值\n",
    "* **函数** 不仅能执行，还可以返回结果\n",
    "\n",
    "### 1.2 面相过程 和 面相对象 基本概念\n",
    "\n",
    "#### 1) **面相过程** —— **怎么做**？\n",
    "\n",
    "1. 把完成某一个需求的 `所有步骤` `从头到尾` 逐步实现\n",
    "2. 根据开发需求，将某些 **功能独立** 的代码 **封装** 成一个又一个 **函数**\n",
    "3. 最后完成的代码，就是顺序地调用 **不同的函数**\n",
    "\n",
    "**特点**\n",
    "\n",
    "1. 注重 **步骤与过程**，不注重职责分工\n",
    "2. 如果需求复杂，代码会变得很复杂\n",
    "3. **开发复杂项目，没有固定的套路，开发难度很大！**\n",
    "\n",
    "![001_面向过程](./res/001_%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B.png)\n",
    "\n",
    "\n",
    "#### 2) **面向对象** —— **谁来做**？\n",
    "\n",
    "> 相比较函数，**面向对象** 是 **更大** 的 **封装**，根据 **职责** 在 **一个对象中 封装 多个方法**\n",
    "\n",
    "1. 在完成某一个需求前，首先确定 **职责** —— **要做的事情（方法）**\n",
    "2. 根据 **职责** 确定不同的 **对象**，在 **对象** 内部封装不同的 **方法**（多个）\n",
    "3. 最后完成的代码，就是顺序地让 **不同的对象** 调用 **不同的方法**\n",
    "\n",
    "**特点**\n",
    "\n",
    "1. 注重 **对象和职责**，不同的对象承担不同的职责\n",
    "2. 更加适合应对复杂的需求变化，**是专门应对复杂项目开发，提供的固定套路**\n",
    "3. **需要在面向过程基础上，再学习一些面向对象的语法**\n",
    "\n",
    "![001_植物大战僵尸](./res/001_%E6%A4%8D%E7%89%A9%E5%A4%A7%E6%88%98%E5%83%B5%E5%B0%B8.png)\n",
    "\n",
    "![001_植物大战僵尸类图](./res/001_%E6%A4%8D%E7%89%A9%E5%A4%A7%E6%88%98%E5%83%B5%E5%B0%B8%E7%B1%BB%E5%9B%BE.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 类和对象\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 类和对象的概念\n",
    "* 类和对象的关系\n",
    "* 类的设计\n",
    "\n",
    "## 01. 类和对象的概念\n",
    "\n",
    "**类** 和 **对象** 是 **面向对象编程的 两个 核心概念**\n",
    "\n",
    "### 1.1 类\n",
    "\n",
    "* **类** 是对一群具有 **相同 特征** 或者 **行为** 的事物的一个统称，是抽象的，**不能直接使用**\n",
    "    * **特征** 被称为 **属性**\n",
    "    * **行为** 被称为 **方法**\n",
    "* **类** 就相当于制造飞机时的**图纸**，是一个 **模板**，是 **负责创建对象的**\n",
    "\n",
    "![002_飞机设计图纸](./res/002_%E9%A3%9E%E6%9C%BA%E8%AE%BE%E8%AE%A1%E5%9B%BE%E7%BA%B8.png)\n",
    "\n",
    "### 1.2 对象\n",
    "\n",
    "* **对象** 是 **由类创建出来的一个具体存在**，可以直接使用\n",
    "* 由 **哪一个类** 创建出来的 **对象**，就拥有在 **哪一个类** 中定义的：\n",
    "    * 属性\n",
    "    * 方法\n",
    "* **对象** 就相当于用 **图纸** **制造** 的飞机\n",
    "\n",
    "> 在程序开发中，应该 **先有类，再有对象**\n",
    "\n",
    "![003_飞机对象](./res/003_%E9%A3%9E%E6%9C%BA%E5%AF%B9%E8%B1%A1.png)\n",
    "\n",
    "## 02. 类和对象的关系\n",
    "\n",
    "* **类是模板**，**对象** 是根据 **类** 这个模板创建出来的，应该 **先有类，再有对象**\n",
    "* **类** 只有一个，而 **对象** 可以有很多个\n",
    "    * **不同的对象** 之间 **属性** 可能会各不相同\n",
    "* **类** 中定义了什么 **属性和方法**，**对象** 中就有什么属性和方法，**不可能多，也不可能少**\n",
    "\n",
    "## 03. 类的设计\n",
    "\n",
    "在使用面相对象开发前，应该首先分析需求，确定一下，程序中需要包含哪些类！\n",
    "\n",
    "![001_植物大战僵尸类图](./res/001_%E6%A4%8D%E7%89%A9%E5%A4%A7%E6%88%98%E5%83%B5%E5%B0%B8%E7%B1%BB%E5%9B%BE.png)\n",
    "\n",
    "在程序开发中，要设计一个类，通常需要满足一下三个要素：\n",
    "\n",
    "1. **类名** 这类事物的名字，**满足大驼峰命名法**\n",
    "2. **属性** 这类事物具有什么样的特征\n",
    "3. **方法** 这类事物具有什么样的行为\n",
    "\n",
    "### 大驼峰命名法\n",
    "\n",
    "`CapWords`\n",
    "\n",
    "1. 每一个单词的首字母大写\n",
    "2. 单词与单词之间没有下划线\n",
    "\n",
    "### 3.1 类名的确定\n",
    "\n",
    "**名词提炼法** 分析 **整个业务流程**，出现的 **名词**，通常就是找到的类\n",
    "\n",
    "### 3.2 属性和方法的确定\n",
    "\n",
    "* 对 **对象的特征描述**，通常可以定义成 **属性**\n",
    "* **对象具有的行为**（动词），通常可以定义成 **方法**\n",
    "\n",
    "> 提示：需求中没有涉及的属性或者方法在设计类时，不需要考虑\n",
    "\n",
    "### 练习 1\n",
    "\n",
    "**需求**\n",
    "\n",
    "* **小明** 今年 **18 岁**，**身高 1.75**，每天早上 **跑** 完步，会去 **吃** 东西\n",
    "* **小美** 今年 **17 岁**，**身高 1.65**，小美不跑步，小美喜欢 **吃** 东西 \n",
    "\n",
    "![002_Person类-w134](./res/002_Person%E7%B1%BB.png)\n",
    "\n",
    "### 练习 2\n",
    "\n",
    "**需求**\n",
    "\n",
    "* 一只 **黄颜色** 的 **狗狗** 叫 **大黄**\n",
    "* 看见生人 **汪汪叫**\n",
    "* 看见家人 **摇尾巴**\n",
    "\n",
    "![003_Dog类-w134](./res/003_Dog%E7%B1%BB.png)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面相对象基础语法\n",
    "\n",
    "## 目标\n",
    "\n",
    "* `dir` 内置函数\n",
    "* 定义简单的类（只包含方法）\n",
    "* 方法中的 `self` 参数\n",
    "* 初始化方法\n",
    "* 内置方法和属性\n",
    "\n",
    "## 01. `dir` 内置函数（知道）\n",
    "\n",
    "* 在 `Python` 中 **对象几乎是无所不在的**，我们之前学习的 **变量**、**数据**、**函数** 都是对象\n",
    "\n",
    "在 `Python` 中可以使用以下两个方法验证：\n",
    "\n",
    "1. 在 **标识符** / **数据** 后输入一个 `.`，然后按下 `TAB` 键，`iPython` 会提示该对象能够调用的 **方法列表**\n",
    "2. 使用内置函数 `dir` 传入 **标识符** / **数据**，可以查看对象内的 **所有属性及方法**\n",
    "\n",
    "**提示** `__方法名__` 格式的方法是 `Python` 提供的 **内置方法 / 属性**，稍后会给大家介绍一些常用的 内置方法 / 属性\n",
    "\n",
    "| 序号 | 方法名 | 类型 | 作用 |\n",
    "| :---: | :---: | :---: | --- |\n",
    "| 01 | `__new__` | 方法 | **创建对象**时，会被 **自动** 调用 |\n",
    "| 02 | `__init__` | 方法 | **对象被初始化**时，会被 **自动** 调用 |\n",
    "| 03 | `__del__` | 方法 | **对象被从内存中销毁**前，会被 **自动** 调用 |\n",
    "| 04 | `__str__` | 方法 | 返回**对象的描述信息**，`print` 函数输出使用 |\n",
    "\n",
    "**提示** 利用好 `dir()` 函数，在学习时很多内容就不需要死记硬背了\n",
    "\n",
    "## 02. 定义简单的类（只包含方法）\n",
    "\n",
    "> **面向对象** 是 **更大** 的 **封装**，在 **一个类中 封装 多个方法**，这样 **通过这个类创建出来的对象，就可以直接调用这些方法了**！\n",
    "\n",
    "### 2.1 定义只包含方法的类\n",
    "\n",
    "* 在 `Python` 中要定义一个只包含方法的类，语法格式如下：\n",
    "```python\n",
    "class 类名:\n",
    "\n",
    "    def 方法1(self, 参数列表):\n",
    "        pass\n",
    "    \n",
    "    def 方法2(self, 参数列表):\n",
    "        pass\n",
    "```\n",
    "\n",
    "* **方法** 的定义格式和之前学习过的**函数** 几乎一样\n",
    "* 区别在于第一个参数必须是 `self`，大家暂时先记住，稍后介绍 `self`\n",
    "\n",
    "> 注意：**类名** 的 命名规则 要符合 **大驼峰命名法**\n",
    "\n",
    "### 2.2 创建对象\n",
    "\n",
    "* 当一个类定义完成之后，要使用这个类来创建对象，语法格式如下：\n",
    "\n",
    "```python\n",
    "对象变量 = 类名()\n",
    "```\n",
    "\n",
    "### 2.3 第一个面向对象程序\n",
    "\n",
    "**需求**\n",
    "\n",
    "* **小猫** 爱 **吃** 鱼，**小猫** 要 **喝** 水\n",
    "\n",
    "**分析**\n",
    "\n",
    "1. 定义一个猫类 `Cat`\n",
    "2. 定义两个方法 `eat` 和 `drink`\n",
    "3. 按照需求 —— 不需要定义属性\n",
    "\n",
    "![004_Cat类1-w134](./res/004_Cat%E7%B1%BB1.png)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小猫在喝水\n",
      "小猫爱吃鱼\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "    \"\"\"这是一个猫类\"\"\"\n",
    "\n",
    "    def eat(self):\n",
    "        print(\"小猫爱吃鱼\")\n",
    "\n",
    "    def drink(self):\n",
    "        print(\"小猫在喝水\")\n",
    "\n",
    "tom = Cat()\n",
    "tom.drink()\n",
    "tom.eat()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 引用概念的强调\n",
    "\n",
    "> 在面向对象开发中，**引用**的概念是同样适用的！\n",
    "\n",
    "* 在 `Python` 中使用类 **创建对象之后**，`tom` 变量中 仍然记录的是 **对象在内存中的地址**\n",
    "* 也就是 `tom` 变量 **引用** 了 **新建的猫对象**\n",
    "* 使用 `print` 输出 **对象变量**，默认情况下，是能够输出这个变量 **引用的对象** 是 **由哪一个类创建的对象**，以及 **在内存中的地址**（**十六进制表示**）\n",
    "\n",
    "> 提示：在计算机中，通常使用 **十六进制** 表示 **内存地址**\n",
    "> \n",
    "> * **十进制** 和 **十六进制** 都是用来表达数字的，只是表示的方式不一样\n",
    "> * **十进制** 和 **十六进制** 的数字之间可以来回转换\n",
    "\n",
    "* `%d` 可以以 **10 进制** 输出数字\n",
    "* `%x` 可以以 **16 进制** 输出数字\n",
    "\n",
    "#### 案例进阶 —— 使用 Cat 类再创建一个对象\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小猫爱吃鱼\n",
      "小猫在喝水\n"
     ]
    }
   ],
   "source": [
    "lazy_cat = Cat()\n",
    "lazy_cat.eat()\n",
    "lazy_cat.drink()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 提问：`tom` 和 `lazy_cat` 是同一个对象吗？\n",
    "\n",
    "## 03. 方法中的 `self` 参数\n",
    "\n",
    "### 3.1 案例改造 —— 给对象增加属性\n",
    "\n",
    "* 在 `Python` 中，要 **给对象设置属性**，非常的容易，**但是不推荐使用**\n",
    "    * 因为：对象属性的封装应该封装在类的内部\n",
    "* 只需要在 **类的外部的代码** 中直接通过 `.` 设置一个属性即可\n",
    "\n",
    "> 注意：这种方式虽然简单，但是不推荐使用！\n",
    "\n",
    "```python\n",
    "tom.name = \"Tom\"\n",
    "...\n",
    "\n",
    "lazy_cat.name = \"大懒猫\"\n",
    "```\n",
    "\n",
    "### 3.2 使用 `self` 在方法内部输出每一只猫的名字\n",
    "\n",
    "> 由 **哪一个对象** 调用的方法，方法内的 `self` 就是 **哪一个对象的引用**\n",
    "\n",
    "* 在类封装的方法内部，`self` 就表示 **当前调用方法的对象自己**\n",
    "* **调用方法时**，程序员不需要传递 `self` 参数\n",
    "* **在方法内部**\n",
    "    * 可以通过 `self.` **访问对象的属性**\n",
    "    * 也可以通过 `self.` **调用其他的对象方法**\n",
    "* 改造代码如下：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom 爱吃鱼\n",
      "大懒猫 爱吃鱼\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def eat(self):\n",
    "        print(\"%s 爱吃鱼\" % self.name)\n",
    "\n",
    "tom = Cat()\n",
    "tom.name = \"Tom\"\n",
    "tom.eat()\n",
    "\n",
    "lazy_cat = Cat()\n",
    "lazy_cat.name = \"大懒猫\"\n",
    "lazy_cat.eat()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![005_方法中的self-w625](./res/005_%E6%96%B9%E6%B3%95%E4%B8%AD%E7%9A%84self.png)\n",
    "\n",
    "* 在 **类的外部**，通过 `变量名.` 访问对象的 **属性和方法**\n",
    "* 在 **类封装的方法中**，通过 `self.` 访问对象的 **属性和方法**\n",
    "\n",
    "## 04. 初始化方法\n",
    "\n",
    "### 4.1 之前代码存在的问题 —— 在类的外部给对象增加属性\n",
    "\n",
    "* 将案例代码进行调整，**先调用方法 再设置属性**，观察一下执行效果\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Cat' object has no attribute 'drink'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-4-11b511152c5e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mtom\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mCat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mtom\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdrink\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m \u001b[0mtom\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0meat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mtom\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m\"Tom\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtom\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mAttributeError\u001b[0m: 'Cat' object has no attribute 'drink'"
     ]
    }
   ],
   "source": [
    "tom = Cat()\n",
    "tom.drink()\n",
    "tom.eat()\n",
    "tom.name = \"Tom\"\n",
    "print(tom)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 程序执行报错如下：\n",
    "\n",
    "```\n",
    "AttributeError: 'Cat' object has no attribute 'name'\n",
    "属性错误：'Cat' 对象没有 'name' 属性\n",
    "```\n",
    "\n",
    "**提示**\n",
    "\n",
    "* 在日常开发中，不推荐在 **类的外部** 给对象增加属性\n",
    "    * 如果**在运行时，没有找到属性，程序会报错**\n",
    "* 对象应该包含有哪些属性，应该 **封装在类的内部**\n",
    "\n",
    "### 4.2 初始化方法\n",
    "\n",
    "* 当使用 `类名()` 创建对象时，会 **自动** 执行以下操作：\n",
    "    1. 为对象在内存中 **分配空间** —— 创建对象\n",
    "    2. 为对象的属性 **设置初始值** —— 初始化方法(`init`)\n",
    "* 这个 **初始化方法** 就是 `__init__` 方法，`__init__` 是对象的**内置方法**\n",
    "\n",
    "> `__init__` 方法是 **专门** 用来定义一个类 **具有哪些属性的方法**！\n",
    "\n",
    "在 `Cat` 中增加 `__init__` 方法，验证该方法在创建对象时会被自动调用\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat:\n",
    "    \"\"\"这是一个猫类\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        print(\"初始化方法\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3 在初始化方法内部定义属性\n",
    "\n",
    "* 在 `__init__` 方法内部使用 `self.属性名 = 属性的初始值` 就可以 **定义属性**\n",
    "* 定义属性之后，再使用 `Cat` 类创建的对象，都会拥有该属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这是一个初始化方法\n",
      "Tom 爱吃鱼\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def __init__(self):\n",
    "\n",
    "        print(\"这是一个初始化方法\")\n",
    "        \n",
    "        # 定义用 Cat 类创建的猫对象都有一个 name 的属性\n",
    "        self.name = \"Tom\"\n",
    "\n",
    "    def eat(self):\n",
    "        print(\"%s 爱吃鱼\" % self.name)\n",
    "\n",
    "# 使用类名()创建对象的时候，会自动调用初始化方法 __init__\n",
    "tom = Cat()\n",
    "\n",
    "tom.eat()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4 改造初始化方法 —— 初始化的同时设置初始值\n",
    "\n",
    "* 在开发中，如果希望在 **创建对象的同时，就设置对象的属性**，可以对 `__init__` 方法进行 **改造**\n",
    "    1. 把希望设置的属性值，定义成 `__init__` 方法的参数\n",
    "    2. 在方法内部使用 `self.属性 = 形参` 接收外部传递的参数\n",
    "    3. 在创建对象时，使用 `类名(属性1, 属性2...)` 调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始化方法 Tom\n",
      "初始化方法 大懒猫\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        print(\"初始化方法 %s\" % name)\n",
    "        self.name = name\n",
    "    \n",
    "tom = Cat(\"Tom\")\n",
    "lazy_cat = Cat(\"大懒猫\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 05. 内置方法和属性\n",
    "\n",
    "| 序号 | 方法名 | 类型 | 作用 |\n",
    "| :---: | :---: | :---: | --- |\n",
    "| 01 | `__del__` | 方法 | **对象被从内存中销毁**前，会被 **自动** 调用 |\n",
    "| 02 | `__str__` | 方法 | 返回**对象的描述信息**，`print` 函数输出使用 |\n",
    "\n",
    "### 5.1 `__del__` 方法（知道）\n",
    "\n",
    "* 在 `Python` 中\n",
    "    * 当使用 `类名()` 创建对象时，为对象 **分配完空间**后，**自动** 调用 `__init__` 方法\n",
    "    * 当一个 **对象被从内存中销毁** 前，会 **自动** 调用 `__del__` 方法\n",
    "\n",
    "* **应用场景**\n",
    "    * `__init__` 改造初始化方法，可以让创建对象更加灵活\n",
    "    * `__del__` 如果希望在对象被销毁前，再做一些事情，可以考虑一下 `__del__` 方法\n",
    "    \n",
    "* **生命周期**\n",
    "    * 一个对象从调用 `类名()` 创建，生命周期开始\n",
    "    * 一个对象的 `__del__` 方法一旦被调用，生命周期结束\n",
    "    * 在对象的生命周期内，可以访问对象属性，或者让对象调用方法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom 来了\n",
      "Tom\n",
      "Tom 去了\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def __init__(self, new_name):\n",
    "\n",
    "        self.name = new_name\n",
    "\n",
    "        print(\"%s 来了\" % self.name)\n",
    "\n",
    "    def __del__(self):\n",
    "\n",
    "        print(\"%s 去了\" % self.name)\n",
    "\n",
    "# tom 是一个全局变量\n",
    "tom = Cat(\"Tom\")\n",
    "print(tom.name)\n",
    "\n",
    "# del 关键字可以删除一个对象\n",
    "del tom\n",
    "\n",
    "print(\"-\" * 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 `__str__` 方法\n",
    "\n",
    "* 在 `Python` 中，使用 `print` 输出 **对象变量**，默认情况下，会输出这个变量 **引用的对象** 是 **由哪一个类创建的对象**，以及 **在内存中的地址**（**十六进制表示**）\n",
    "* 如果在开发中，希望使用 `print` 输出 **对象变量** 时，能够打印 **自定义的内容**，就可以利用 `__str__` 这个内置方法了\n",
    "\n",
    "> 注意：`__str__` 方法必须返回一个字符串\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom 来了\n",
      "我是小猫：Tom\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def __init__(self, new_name):\n",
    "\n",
    "        self.name = new_name\n",
    "\n",
    "        print(\"%s 来了\" % self.name)\n",
    "\n",
    "    def __del__(self):\n",
    "\n",
    "        print(\"%s 去了\" % self.name)\n",
    "\n",
    "    def __str__(self):\n",
    "        return \"我是小猫：%s\" % self.name\n",
    "\n",
    "tom = Cat(\"Tom\")\n",
    "print(tom)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象封装案例\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 封装\n",
    "* 小明爱跑步\n",
    "* 存放家具\n",
    "\n",
    "### 01. 封装\n",
    "\n",
    "1. **封装** 是面向对象编程的一大特点\n",
    "2. 面向对象编程的 **第一步** —— 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中\n",
    "3. **外界** 使用 **类** 创建 **对象**，然后 **让对象调用方法**\n",
    "4. **对象方法的细节** 都被 **封装** 在 **类的内部**\n",
    "\n",
    "## 02. 小明爱跑步\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. **小明** **体重** `75.0` 公斤\n",
    "2. 小明每次 **跑步** 会减肥 `0.5` 公斤\n",
    "3. 小明每次 **吃东西** 体重增加 `1` 公斤\n",
    "\n",
    "![007_小明爱跑步-w328](./res/007_%E5%B0%8F%E6%98%8E%E7%88%B1%E8%B7%91%E6%AD%A5.png)\n",
    "\n",
    "> 提示：在 **对象的方法内部**，是可以 **直接访问对象的属性** 的！\n",
    "\n",
    "* 代码实现：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小明 爱跑步，跑步锻炼身体\n",
      "小明 是吃货，吃完这顿再减肥\n",
      "小明 是吃货，吃完这顿再减肥\n",
      "我的名字叫 小明 体重 76.50 公斤\n"
     ]
    }
   ],
   "source": [
    "class Person:\n",
    "    \"\"\"人类\"\"\"\n",
    "\n",
    "    def __init__(self, name, weight):\n",
    "\n",
    "        self.name = name\n",
    "        self.weight = weight\n",
    "\n",
    "    def __str__(self):\n",
    "\n",
    "        return \"我的名字叫 %s 体重 %.2f 公斤\" % (self.name, self.weight)\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"跑步\"\"\"\n",
    "\n",
    "        print(\"%s 爱跑步，跑步锻炼身体\" % self.name)\n",
    "        self.weight -= 0.5\n",
    "\n",
    "    def eat(self):\n",
    "        \"\"\"吃东西\"\"\"\n",
    "\n",
    "        print(\"%s 是吃货，吃完这顿再减肥\" % self.name)\n",
    "        self.weight += 1\n",
    "\n",
    "\n",
    "xiaoming = Person(\"小明\", 75)\n",
    "\n",
    "xiaoming.run()\n",
    "xiaoming.eat()\n",
    "xiaoming.eat()\n",
    "\n",
    "print(xiaoming)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 小明爱跑步扩展 —— 小美也爱跑步\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. **小明** 和 **小美** 都爱跑步\n",
    "2. **小明** **体重** `75.0` 公斤\n",
    "3. **小美** **体重** `45.0` 公斤\n",
    "4. 每次 **跑步** 都会减少 `0.5` 公斤\n",
    "5. 每次 **吃东西** 都会增加 `1` 公斤\n",
    "\n",
    "![007_小明爱跑步-w328](./res/007_%E5%B0%8F%E6%98%8E%E7%88%B1%E8%B7%91%E6%AD%A5.png)\n",
    "\n",
    "**提示**\n",
    "\n",
    "1. 在 **对象的方法内部**，是可以 **直接访问对象的属性** 的\n",
    "2. **同一个类** 创建的 **多个对象** 之间，**属性** 互不干扰！\n",
    "\n",
    "![001_植物大战僵尸](./res/001_%E6%A4%8D%E7%89%A9%E5%A4%A7%E6%88%98%E5%83%B5%E5%B0%B8.png)\n",
    "\n",
    "\n",
    "## 03. 摆放家具\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. **房子(House)** 有 **户型**、**总面积** 和 **家具名称列表**\n",
    "    * 新房子没有任何的家具\n",
    "2. **家具(HouseItem)** 有 **名字** 和 **占地面积**，其中\n",
    "    *  **席梦思(bed)** 占地 `4` 平米\n",
    "    *  **衣柜(chest)** 占地 `2` 平米\n",
    "    *  **餐桌(table)** 占地 `1.5` 平米\n",
    "3. 将以上三件 **家具** **添加** 到 **房子** 中\n",
    "4. 打印房子时，要求输出：**户型**、**总面积**、**剩余面积**、**家具名称列表**\n",
    "\n",
    "![008_摆放家具-w723](./res/008_%E6%91%86%E6%94%BE%E5%AE%B6%E5%85%B7.png)\n",
    "\n",
    "**剩余面积**\n",
    "\n",
    "1. 在创建房子对象时，定义一个 **剩余面积的属性**，**初始值和总面积相等**\n",
    "2. 当调用 `add_item` 方法，向房间 **添加家具** 时，让 **剩余面积** -= **家具面积**\n",
    "\n",
    "**思考**：应该先开发哪一个类？\n",
    "\n",
    "**答案** —— **家具类**\n",
    "\n",
    "1. 家具简单\n",
    "2. 房子要使用到家具，**被使用的类**，通常应该先开发\n",
    "\n",
    "### 3.1 创建家具\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[席梦思] 占地面积 4.00\n",
      "[衣柜] 占地面积 2.00\n",
      "[餐桌] 占地面积 1.50\n"
     ]
    }
   ],
   "source": [
    "class HouseItem:\n",
    "\n",
    "    def __init__(self, name, area):\n",
    "        \"\"\"\n",
    "\n",
    "        :param name: 家具名称\n",
    "        :param area: 占地面积\n",
    "        \"\"\"\n",
    "        self.name = name\n",
    "        self.area = area\n",
    "\n",
    "    def __str__(self):\n",
    "        return \"[%s] 占地面积 %.2f\" % (self.name, self.area)\n",
    "\n",
    "\n",
    "# 1. 创建家具\n",
    "bed = HouseItem(\"席梦思\", 4)\n",
    "chest = HouseItem(\"衣柜\", 2)\n",
    "table = HouseItem(\"餐桌\", 1.5)\n",
    "\n",
    "print(bed)\n",
    "print(chest)\n",
    "print(table)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小结**\n",
    "\n",
    "1. 创建了一个 **家具类**，使用到 `__init__` 和 `__str__` 两个内置方法\n",
    "2. 使用 **家具类** 创建了 **三个家具对象**，并且 **输出家具信息**\n",
    "\n",
    "### 3.2 创建房间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "要添加 [席梦思] 占地面积 4.00\n",
      "要添加 [衣柜] 占地面积 2.00\n",
      "要添加 [餐桌] 占地面积 1.50\n",
      "户型：两室一厅\n",
      "总面积：60.00[剩余：60.00]\n",
      "家具：[]\n"
     ]
    }
   ],
   "source": [
    "class House:\n",
    "\n",
    "    def __init__(self, house_type, area):\n",
    "        \"\"\"\n",
    "\n",
    "        :param house_type: 户型\n",
    "        :param area: 总面积\n",
    "        \"\"\"\n",
    "        self.house_type = house_type\n",
    "        self.area = area\n",
    "        \n",
    "        # 剩余面积默认和总面积一致\n",
    "        self.free_area = area\n",
    "        # 默认没有任何的家具\n",
    "        self.item_list = []\n",
    "\n",
    "    def __str__(self):\n",
    "\n",
    "        # Python 能够自动的将一对括号内部的代码连接在一起\n",
    "        return (\"户型：%s\\n总面积：%.2f[剩余：%.2f]\\n家具：%s\"\n",
    "                % (self.house_type, self.area,\n",
    "                   self.free_area, self.item_list))\n",
    "\n",
    "    def add_item(self, item):\n",
    "\n",
    "        print(\"要添加 %s\" % item)\n",
    "\n",
    "# 2. 创建房子对象\n",
    "my_home = House(\"两室一厅\", 60)\n",
    "\n",
    "my_home.add_item(bed)\n",
    "my_home.add_item(chest)\n",
    "my_home.add_item(table)\n",
    "\n",
    "print(my_home)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小结**\n",
    "\n",
    "1. 创建了一个 **房子类**，使用到 `__init__` 和 `__str__` 两个内置方法\n",
    "2. 准备了一个 `add_item` 方法 **准备添加家具**\n",
    "3. 使用 **房子类** 创建了 **一个房子对象**\n",
    "4. 让 **房子对象** 调用了三次 `add_item` 方法，将 **三件家具** 以实参传递到 `add_item` 内部\n",
    "\n",
    "### 3.3 添加家具\n",
    "\n",
    "**需求**\n",
    "\n",
    "* 1> **判断** **家具的面积** 是否 **超过剩余面积**，**如果超过**，提示不能添加这件家具\n",
    "* 2> 将 **家具的名称** 追加到 **家具名称列表** 中\n",
    "* 3> 用 **房子的剩余面积** - **家具面积**\n",
    "\n",
    "\n",
    "```python\n",
    "    def add_item(self, item):\n",
    "\n",
    "        print(\"要添加 %s\" % item)\n",
    "        # 1. 判断家具面积是否大于剩余面积\n",
    "        if item.area > self.free_area:\n",
    "            print(\"%s 的面积太大，不能添加到房子中\" % item.name)\n",
    "\n",
    "            return\n",
    "\n",
    "        # 2. 将家具的名称追加到名称列表中\n",
    "        self.item_list.append(item.name)\n",
    "\n",
    "        # 3. 计算剩余面积\n",
    "        self.free_area -= item.area\n",
    "```\n",
    "\n",
    "### 3.4 小结\n",
    "\n",
    "* 主程序只负责创建 **房子** 对象和 **家具** 对象\n",
    "* 让 **房子** 对象调用 `add_item` 方法 **将家具添加到房子**中\n",
    "* **面积计算**、**剩余面积**、**家具列表** 等处理都被 **封装** 到 **房子类的内部**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象封装案例 II\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 士兵突击案例\n",
    "* 身份运算符\n",
    "\n",
    "**封装**\n",
    "\n",
    "1. **封装** 是面向对象编程的一大特点\n",
    "2. 面向对象编程的 **第一步** —— 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中\n",
    "3. **外界** 使用 **类** 创建 **对象**，然后 **让对象调用方法**\n",
    "4. **对象方法的细节** 都被 **封装** 在 **类的内部**\n",
    "\n",
    "> 一个对象的 **属性** 可以是 **另外一个类创建的对象**\n",
    "\n",
    "## 01. 士兵突击\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. **士兵** **许三多** 有一把 **AK47**\n",
    "2. **士兵** 可以 **开火**\n",
    "3. **枪** 能够 **发射** 子弹\n",
    "4. **枪** 装填 **装填子弹** —— **增加子弹数量**\n",
    "\n",
    "![009_士兵突击-w610](./res/009_%E5%A3%AB%E5%85%B5%E7%AA%81%E5%87%BB.png)\n",
    "\n",
    "### 1.1 开发枪类\n",
    "\n",
    "**`shoot` 方法需求**\n",
    "\n",
    "* 1> 判断是否有子弹，没有子弹无法射击\n",
    "* 2> 使用 `print` 提示射击，并且输出子弹数量\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ak47 发射子弹[49]...\n"
     ]
    }
   ],
   "source": [
    "class Gun:\n",
    "\n",
    "    def __init__(self, model):\n",
    "\n",
    "        # 枪的型号\n",
    "        self.model = model\n",
    "        # 子弹数量\n",
    "        self.bullet_count = 0\n",
    "\n",
    "    def add_bullet(self, count):\n",
    "\n",
    "        self.bullet_count += count\n",
    "\n",
    "    def shoot(self):\n",
    "\n",
    "        # 判断是否还有子弹\n",
    "        if self.bullet_count <= 0:\n",
    "            print(\"没有子弹了...\")\n",
    "\n",
    "            return\n",
    "\n",
    "        # 发射一颗子弹\n",
    "        self.bullet_count -= 1\n",
    "        \n",
    "        print(\"%s 发射子弹[%d]...\" % (self.model, self.bullet_count))\n",
    "\n",
    "# 创建枪对象\n",
    "ak47 = Gun(\"ak47\")\n",
    "ak47.add_bullet(50)\n",
    "ak47.shoot()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 开发士兵类\n",
    "\n",
    "> 假设：每一个新兵 都 **没有枪**\n",
    "\n",
    "**定义没有初始值的属性**\n",
    "\n",
    "在定义属性时，如果 **不知道设置什么初始值**，可以设置为 `None`\n",
    "\n",
    "* `None` **关键字** 表示 **什么都没有**\n",
    "* 表示一个 **空对象**，**没有方法和属性，是一个特殊的常量**\n",
    "* 可以将 `None` 赋值给任何一个变量\n",
    "\n",
    "**`fire` 方法需求**\n",
    "\n",
    "* 1> 判断是否有枪，没有枪没法冲锋\n",
    "* 2> 喊一声口号\n",
    "* 3> 装填子弹\n",
    "* 4> 射击"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Soldier:\n",
    "\n",
    "    def __init__(self, name):\n",
    "\n",
    "        # 姓名\n",
    "        self.name = name\n",
    "        # 枪，士兵初始没有枪 None 关键字表示什么都没有\n",
    "        self.gun = None\n",
    "\n",
    "    def fire(self):\n",
    "\n",
    "        # 1. 判断士兵是否有枪\n",
    "        if self.gun is None:\n",
    "            print(\"[%s] 还没有枪...\" % self.name)\n",
    "\n",
    "            return\n",
    "\n",
    "        # 2. 高喊口号\n",
    "        print(\"冲啊...[%s]\" % self.name)\n",
    "\n",
    "        # 3. 让枪装填子弹\n",
    "        self.gun.add_bullet(50)\n",
    "\n",
    "        # 4. 让枪发射子弹\n",
    "        self.gun.shoot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小结**\n",
    "\n",
    "1. 创建了一个 **士兵类**，使用到 `__init__` 内置方法\n",
    "2. 在定义属性时，如果 **不知道设置什么初始值**，可以设置为 `None`\n",
    "3. 在 **封装的** 方法内部，还可以让 **自己的** **使用其他类创建的对象属性** 调用已经 **封装好的方法** \n",
    "\n",
    "## 02. 身份运算符\n",
    "\n",
    "身份运算符用于 **比较** 两个对象的 **内存地址** 是否一致 —— **是否是对同一个对象的引用**\n",
    "\n",
    "* 在 `Python` 中针对 `None` 比较时，建议使用 `is` 判断\n",
    "\n",
    "| 运算符 | 描述 | 实例 |\n",
    "| --- | --- | --- |\n",
    "| is | is 是判断两个标识符是不是引用同一个对象 | x is y，类似 id(x) == id(y) |\n",
    "| is not | is not 是判断两个标识符是不是引用不同对象 | x is not y，类似 id(a) != id(b) |\n",
    "\n",
    "### is 与 == 区别：\n",
    "\n",
    "`is` 用于判断 **两个变量 引用对象是否为同一个** \n",
    "`==` 用于判断 **引用变量的值** 是否相等\n",
    "\n",
    "```python\n",
    ">>> a = [1, 2, 3]\n",
    ">>> b = [1, 2, 3]\n",
    ">>> b is a \n",
    "False\n",
    ">>> b == a\n",
    "True\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 私有属性和私有方法\n",
    "\n",
    "## 01. 应用场景及定义方式\n",
    "\n",
    "**应用场景**\n",
    "\n",
    "* 在实际开发中，**对象** 的 **某些属性或方法** 可能只希望 **在对象的内部被使用**，而 **不希望在外部被访问到**\n",
    "* **私有属性** 就是 **对象** 不希望公开的 **属性**\n",
    "* **私有方法** 就是 **对象** 不希望公开的 **方法**\n",
    "\n",
    "**定义方式**\n",
    "\n",
    "* 在 **定义属性或方法时**，在 **属性名或者方法名前** 增加 **两个下划线**，定义的就是 **私有** 属性或方法\n",
    "\n",
    "![010_私有属性和方法-w265](./res/010_%E7%A7%81%E6%9C%89%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3%95.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Women:\n",
    "\n",
    "    def __init__(self, name):\n",
    "\n",
    "        self.name = name\n",
    "        # 不要问女生的年龄\n",
    "        self.__age = 18\n",
    "\n",
    "    def __secret(self):\n",
    "        print(\"我的年龄是 %d\" % self.__age)\n",
    "\n",
    "\n",
    "xiaofang = Women(\"小芳\")\n",
    "# 私有属性，外部不能直接访问\n",
    "# print(xiaofang.__age)\n",
    "\n",
    "# 私有方法，外部不能直接调用\n",
    "# xiaofang.__secret()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 02. 伪私有属性和私有方法（科普）\n",
    "\n",
    "> 提示：在日常开发中，**不要使用这种方式**，**访问对象的 私有属性 或 私有方法**\n",
    "\n",
    "`Python` 中，并没有 **真正意义** 的 **私有**\n",
    "\n",
    "* 在给 **属性**、**方法** 命名时，实际是对 **名称** 做了一些特殊处理，使得外界无法访问到\n",
    "* **处理方式**：在 **名称** 前面加上 `_类名` => `_类名__名称`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18\n",
      "我的年龄是 18\n"
     ]
    }
   ],
   "source": [
    "# 私有属性，外部不能直接访问到\n",
    "print(xiaofang._Women__age)\n",
    "\n",
    "# 私有方法，外部不能直接调用\n",
    "xiaofang._Women__secret()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 单例\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 单例设计模式\n",
    "* `__new__` 方法\n",
    "* Python 中的单例\n",
    "\n",
    "## 01. 单例设计模式\n",
    "\n",
    "* 设计模式\n",
    "    * **设计模式** 是 **前人工作的总结和提炼**，通常，被人们广泛流传的设计模式都是针对 **某一特定问题** 的成熟的解决方案\n",
    "    * 使用 **设计模式** 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性\n",
    "\n",
    "* 单例设计模式\n",
    "    * **目的** —— 让 **类** 创建的对象，在系统中 **只有** **唯一的一个实例**\n",
    "    * 每一次执行 `类名()` 返回的对象，**内存地址是相同的**\n",
    "\n",
    "### 单例设计模式的应用场景\n",
    "\n",
    "* **音乐播放** 对象\n",
    "* **回收站** 对象\n",
    "* **打印机** 对象\n",
    "* ……\n",
    "\n",
    "## 02. `__new__` 方法\n",
    "\n",
    "* 使用 **类名()** 创建对象时，`Python` 的解释器 **首先** 会 调用 `__new__` 方法为对象 **分配空间**\n",
    "* `__new__` 是一个 由 `object` 基类提供的 **内置的静态方法**，主要作用有两个：\n",
    "    * 1) 在内存中为对象 **分配空间**\n",
    "    * 2) **返回** 对象的引用\n",
    "* `Python` 的解释器获得对象的 **引用** 后，将引用作为 **第一个参数**，传递给 `__init__` 方法\n",
    "\n",
    "> 重写 `__new__` 方法 的代码非常固定！\n",
    "\n",
    "* 重写 `__new__` 方法 **一定要** `return super().__new__(cls)` \n",
    "* 否则 Python 的解释器 **得不到** 分配了空间的 **对象引用**，**就不会调用对象的初始化方法**\n",
    "* 注意：`__new__` 是一个静态方法，在调用时需要 **主动传递** `cls` 参数\n",
    "\n",
    "![022_对象分配空间和初始化-w838](./res/022_%E5%AF%B9%E8%B1%A1%E5%88%86%E9%85%8D%E7%A9%BA%E9%97%B4%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96.png)\n",
    "\n",
    "**示例代码**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始化音乐播放对象\n",
      "<__main__.MusicPlayer object at 0x000001AC89D76C18>\n"
     ]
    }
   ],
   "source": [
    "class MusicPlayer(object):\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        # 如果不返回任何结果，\n",
    "        return super().__new__(cls)\n",
    "\n",
    "    def __init__(self):\n",
    "        print(\"初始化音乐播放对象\")\n",
    "\n",
    "player = MusicPlayer()\n",
    "\n",
    "print(player)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 03. Python 中的单例\n",
    "\n",
    "* **单例** —— 让 **类** 创建的对象，在系统中 **只有** **唯一的一个实例**\n",
    "    1. 定义一个 **类属性**，初始值是 `None`，用于记录 **单例对象的引用**\n",
    "    2. 重写 `__new__` 方法\n",
    "    3. 如果 **类属性** `is None`，调用父类方法分配空间，并在类属性中记录结果\n",
    "    4. 返回 **类属性** 中记录的 **对象引用**\n",
    "\n",
    "![023_单例流程-w893](./res/023_%E5%8D%95%E4%BE%8B%E6%B5%81%E7%A8%8B.png)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MusicPlayer(object):\n",
    "\n",
    "    # 定义类属性记录单例对象引用\n",
    "    instance = None\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "\n",
    "        # 1. 判断类属性是否已经被赋值\n",
    "        if cls.instance is None:\n",
    "            cls.instance = super().__new__(cls)\n",
    "\n",
    "        # 2. 返回类属性的单例引用\n",
    "        return cls.instance\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 只执行一次初始化工作\n",
    "\n",
    "* 在每次使用 `类名()` 创建对象时，`Python` 的解释器都会自动调用两个方法：\n",
    "    * `__new__` 分配空间\n",
    "    * `__init__` 对象初始化\n",
    "* 在上一小节对 `__new__` 方法改造之后，每次都会得到 **第一次被创建对象的引用**\n",
    "* 但是：**初始化方法还会被再次调用**\n",
    "\n",
    "**需求**\n",
    "\n",
    "* 让 **初始化动作** 只被 **执行一次**\n",
    "\n",
    "**解决办法**\n",
    "\n",
    "1. 定义一个类属性 `init_flag` 标记是否 **执行过初始化动作**，初始值为 `False`\n",
    "2. 在 `__init__` 方法中，判断 `init_flag`，如果为 `False` 就执行初始化动作\n",
    "3. 然后将 `init_flag` 设置为 `True`\n",
    "4. 这样，再次 **自动** 调用 `__init__` 方法时，**初始化动作就不会被再次执行** 了\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始化音乐播放器\n",
      "<__main__.MusicPlayer object at 0x000001AC89CB8588>\n",
      "<__main__.MusicPlayer object at 0x000001AC89CB8588>\n"
     ]
    }
   ],
   "source": [
    "class MusicPlayer(object):\n",
    "\n",
    "    # 记录第一个被创建对象的引用\n",
    "    instance = None\n",
    "    # 记录是否执行过初始化动作\n",
    "    init_flag = False\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "\n",
    "        # 1. 判断类属性是否是空对象\n",
    "        if cls.instance is None:\n",
    "            # 2. 调用父类的方法，为第一个对象分配空间\n",
    "            cls.instance = super().__new__(cls)\n",
    "\n",
    "        # 3. 返回类属性保存的对象引用\n",
    "        return cls.instance\n",
    "\n",
    "    def __init__(self):\n",
    "\n",
    "        if not MusicPlayer.init_flag:\n",
    "            print(\"初始化音乐播放器\")\n",
    "\n",
    "            MusicPlayer.init_flag = True\n",
    "\n",
    "\n",
    "# 创建多个对象\n",
    "player1 = MusicPlayer()\n",
    "print(player1)\n",
    "\n",
    "player2 = MusicPlayer()\n",
    "print(player2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多态\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 多态\n",
    "\n",
    "**面向对象三大特性**\n",
    "\n",
    "1. **封装** 根据 **职责** 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中\n",
    "    \n",
    "    * 定义类的准则 \n",
    "2. **继承** **实现代码的重用**，相同的代码不需要重复的编写\n",
    "    * 设计类的技巧 \n",
    "    * 子类针对自己特有的需求，编写特定的代码\n",
    "3. **多态** 不同的 **子类对象** 调用相同的 **父类方法**，产生不同的执行结果\n",
    "    * **多态** 可以 **增加代码的灵活度**\n",
    "    * 以 **继承** 和 **重写父类方法** 为前提\n",
    "    * 是调用方法的技巧，**不会影响到类的内部设计**\n",
    "\n",
    "    ![016_多态示意图-w384](./res/016_%E5%A4%9A%E6%80%81%E7%A4%BA%E6%84%8F%E5%9B%BE.png)\n",
    "\n",
    "## 多态案例演练\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. 在 `Dog` 类中封装方法 `game`\n",
    "    * 普通狗只是简单的玩耍\n",
    "2. 定义 `XiaoTianDog` 继承自 `Dog`，并且重写 `game` 方法\n",
    "    * 哮天犬需要在天上玩耍\n",
    "3. 定义 `Person` 类，并且封装一个 **和狗玩** 的方法\n",
    "    * 在方法内部，直接让 **狗对象** 调用 `game` 方法\n",
    "\n",
    "![016_多态-w701](./res/016_%E5%A4%9A%E6%80%81.png)\n",
    "\n",
    "**案例小结**\n",
    "\n",
    "* `Person` 类中只需要让 **狗对象** 调用 `game` 方法，而不关心具体是 **什么狗**\n",
    "    * `game` 方法是在 `Dog` 父类中定义的\n",
    "* 在程序执行时，传入不同的 **狗对象** 实参，就会产生不同的执行效果\n",
    "\n",
    "> **多态** 更容易编写出出通用的代码，做出通用的编程，以适应需求的不断变化！\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小明 和 飞天旺财 快乐的玩耍...\n",
      "飞天旺财 飞到天上去玩耍...\n"
     ]
    }
   ],
   "source": [
    "class Dog(object):\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def game(self):\n",
    "        print(\"%s 蹦蹦跳跳的玩耍...\" % self.name)\n",
    "\n",
    "\n",
    "class XiaoTianDog(Dog):\n",
    "\n",
    "    def game(self):\n",
    "        print(\"%s 飞到天上去玩耍...\" % self.name)\n",
    "\n",
    "\n",
    "class Person(object):\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def game_with_dog(self, dog):\n",
    "\n",
    "        print(\"%s 和 %s 快乐的玩耍...\" % (self.name, dog.name))\n",
    "\n",
    "        # 让狗玩耍\n",
    "        dog.game()\n",
    "\n",
    "\n",
    "# 1. 创建一个狗对象\n",
    "# wangcai = Dog(\"旺财\")\n",
    "wangcai = XiaoTianDog(\"飞天旺财\")\n",
    "\n",
    "# 2. 创建一个小明对象\n",
    "xiaoming = Person(\"小明\")\n",
    "\n",
    "# 3. 让小明调用和狗玩的方法\n",
    "xiaoming.game_with_dog(wangcai)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 继承\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 单继承\n",
    "* 多继承\n",
    "\n",
    "**面向对象三大特性**\n",
    "\n",
    "1. **封装** 根据 **职责** 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中\n",
    "2. **继承** **实现代码的重用**，相同的代码不需要重复的编写\n",
    "3. **多态** 不同的对象调用相同的方法，产生不同的执行结果，**增加代码的灵活度**\n",
    "\n",
    "## 01. 单继承\n",
    "\n",
    "### 1.1 继承的概念、语法和特点\n",
    "\n",
    "**继承的概念**：**子类** 拥有 **父类** 的所有 **方法** 和 **属性**\n",
    "\n",
    "![011_继承对比图示](./res/011_%E7%BB%A7%E6%89%BF%E5%AF%B9%E6%AF%94%E5%9B%BE%E7%A4%BA.png)\n",
    "\n",
    "#### 1) 继承的语法\n",
    "```python\n",
    "class 类名(父类名):\n",
    "\n",
    "    pass\n",
    "```\n",
    "\n",
    "* **子类** 继承自 **父类**，可以直接 **享受** 父类中已经封装好的方法，不需要再次开发\n",
    "* **子类** 中应该根据 **职责**，封装 **子类特有的** **属性和方法**\n",
    "\n",
    "#### 2) 专业术语\n",
    "\n",
    "* `Dog` 类是 `Animal` 类的**子类**，`Animal` 类是 `Dog` 类的**父类**，`Dog` 类从 `Animal` 类**继承**\n",
    "* `Dog` 类是 `Animal` 类的**派生类**，`Animal` 类是 `Dog` 类的**基类**，`Dog` 类从 `Animal` 类**派生**\n",
    "\n",
    "#### 3) 继承的传递性\n",
    "\n",
    "* `C` 类从 `B` 类继承，`B` 类又从 `A` 类继承\n",
    "* 那么 `C` 类就具有 `B` 类和 `A` 类的所有属性和方法\n",
    "\n",
    "**子类** 拥有 **父类** 以及 **父类的父类** 中封装的所有 **属性** 和 **方法**\n",
    "\n",
    "**提问**\n",
    "\n",
    "**哮天犬** 能够调用 `Cat` 类中定义的 `catch` 方法吗？\n",
    "\n",
    "**答案**\n",
    "\n",
    "**不能**，因为 **哮天犬** 和 `Cat` 之间没有 **继承** 关系\n",
    "\n",
    "### 1.2 方法的重写\n",
    "\n",
    "* **子类** 拥有 **父类** 的所有 **方法** 和 **属性**\n",
    "* **子类** 继承自 **父类**，可以直接 **享受** 父类中已经封装好的方法，不需要再次开发\n",
    "\n",
    "**应用场景**\n",
    "\n",
    "* 当 **父类** 的方法实现不能满足子类需求时，可以对方法进行 **重写(override)**\n",
    "\n",
    "![012_继承方法的重写-w203](./res/012_%E7%BB%A7%E6%89%BF%E6%96%B9%E6%B3%95%E7%9A%84%E9%87%8D%E5%86%99.png)\n",
    "\n",
    "**重写** 父类方法有两种情况：\n",
    "\n",
    "1. **覆盖** 父类的方法\n",
    "2. 对父类方法进行 **扩展**\n",
    "\n",
    "#### 1) 覆盖父类的方法\n",
    "\n",
    "* 如果在开发中，**父类的方法实现** 和 **子类的方法实现**，**完全不同**\n",
    "* 就可以使用 **覆盖** 的方式，**在子类中** **重新编写** 父类的方法实现\n",
    "\n",
    "> 具体的实现方式，就相当于在 **子类中** 定义了一个 **和父类同名的方法并且实现**\n",
    "\n",
    "重写之后，在运行时，**只会调用** 子类中重写的方法，而不再会调用 **父类封装的方法**\n",
    "\n",
    "#### 2) 对父类方法进行 **扩展**\n",
    "\n",
    "* 如果在开发中，**子类的方法实现** 中 **包含** **父类的方法实现**\n",
    "    * **父类原本封装的方法实现** 是 **子类方法的一部分**\n",
    "* 就可以使用 **扩展** 的方式\n",
    "    1. **在子类中** **重写** 父类的方法\n",
    "    2. 在需要的位置使用 `super().父类方法` 来调用父类方法的执行\n",
    "    3. 代码其他的位置针对子类的需求，编写 **子类特有的代码实现**\n",
    "\n",
    "##### 关于 `super`\n",
    "\n",
    "* 在 `Python` 中 `super` 是一个 **特殊的类**\n",
    "* `super()` 就是使用 `super` 类创建出来的对象\n",
    "* **最常** 使用的场景就是在 **重写父类方法时**，调用 **在父类中封装的方法实现**\n",
    "\n",
    "##### 调用父类方法的另外一种方式（知道）\n",
    "\n",
    "> 在 `Python 2.x` 时，如果需要调用父类的方法，还可以使用以下方式：\n",
    "\n",
    "```python\n",
    "父类名.方法(self)\n",
    "```\n",
    "\n",
    "* 这种方式，目前在 `Python 3.x` 还支持这种方式\n",
    "* 这种方法 **不推荐使用**，因为一旦 **父类发生变化**，方法调用位置的 **类名** 同样需要修改\n",
    "\n",
    "**提示**\n",
    "\n",
    "* 在开发时，`父类名` 和 `super()` 两种方式不要混用\n",
    "* 如果使用 **当前子类名** 调用方法，会形成递归调用，**出现死循环**\n",
    "\n",
    "### 1.3 父类的 私有属性 和 私有方法\n",
    "\n",
    "1. **子类对象** **不能** 在自己的方法内部，**直接** 访问 父类的 **私有属性** 或 **私有方法**\n",
    "2. **子类对象** 可以通过 **父类** 的 **公有方法** **间接** 访问到 **私有属性** 或 **私有方法**\n",
    "\n",
    "> * **私有属性、方法** 是对象的隐私，不对外公开，**外界** 以及 **子类** 都不能直接访问\n",
    "> * **私有属性、方法** 通常用于做一些内部的事情\n",
    "\n",
    "**示例**\n",
    "\n",
    "![013_父类的私有属性和私有方法-w220](./res/013_%E7%88%B6%E7%B1%BB%E7%9A%84%E7%A7%81%E6%9C%89%E5%B1%9E%E6%80%A7%E5%92%8C%E7%A7%81%E6%9C%89%E6%96%B9%E6%B3%95.png)\n",
    "\n",
    "* `B` 的对象不能直接访问 `__num2` 属性\n",
    "* `B` 的对象不能在 `demo` 方法内访问 `__num2` 属性\n",
    "* `B` 的对象可以在 `demo` 方法内，调用父类的 `test` 方法\n",
    "* 父类的 `test` 方法内部，能够访问 `__num2` 属性和 `__test` 方法\n",
    "\n",
    "## 02. 多继承\n",
    "\n",
    "**概念**\n",
    "\n",
    "* **子类** 可以拥有 **多个父类**，并且具有 **所有父类** 的 **属性** 和 **方法**\n",
    "* 例如：**孩子** 会继承自己 **父亲** 和 **母亲** 的 **特性**\n",
    "\n",
    "![014_多继承-w384](./res/014_%E5%A4%9A%E7%BB%A7%E6%89%BF.png)\n",
    "\n",
    "**语法**\n",
    "\n",
    "```python\n",
    "class 子类名(父类名1, 父类名2...)\n",
    "    pass\n",
    "```\n",
    "\n",
    "### 2.1 多继承的使用注意事项\n",
    "\n",
    "**问题的提出**\n",
    "\n",
    "* 如果 **不同的父类** 中存在 **同名的方法**，**子类对象** 在调用方法时，会调用 **哪一个父类中**的方法呢？\n",
    "\n",
    "> 提示：**开发时，应该尽量避免这种容易产生混淆的情况！** —— 如果 **父类之间** 存在 **同名的属性或者方法**，应该 **尽量避免** 使用多继承\n",
    "\n",
    "![015_多继承II-w384](./res/015_%E5%A4%9A%E7%BB%A7%E6%89%BFII.png)\n",
    "\n",
    "#### Python 中的 MRO —— 方法搜索顺序（知道）\n",
    "\n",
    "* `Python` 中针对 **类** 提供了一个 **内置属性** `__mro__` 可以查看 **方法** 搜索顺序\n",
    "* MRO 是 `method resolution order`，主要用于 **在多继承时判断 方法、属性 的调用 路径**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'C' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-21-1cbf23019dde>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mC\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__mro__\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'C' is not defined"
     ]
    }
   ],
   "source": [
    "print(C.__mro__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 在搜索方法时，是按照 `__mro__` 的输出结果 **从左至右** 的顺序查找的\n",
    "* 如果在当前类中 **找到方法，就直接执行，不再搜索**\n",
    "* 如果 **没有找到，就查找下一个类** 中是否有对应的方法，**如果找到，就直接执行，不再搜索**\n",
    "* 如果找到最后一个类，还没有找到方法，程序报错\n",
    "\n",
    "### 2.2 新式类与旧式（经典）类\n",
    "\n",
    "> `object` 是 `Python` 为所有对象提供的 **基类**，提供有一些内置的属性和方法，可以使用 `dir` 函数查看\n",
    "\n",
    "* **新式类**：以 `object` 为基类的类，**推荐使用**\n",
    "* **经典类**：不以 `object` 为基类的类，**不推荐使用**\n",
    "\n",
    "* 在 `Python 3.x` 中定义类时，如果没有指定父类，会 **默认使用** `object` 作为该类的 **基类** —— `Python 3.x` 中定义的类都是 **新式类**\n",
    "* 在 `Python 2.x` 中定义类时，如果没有指定父类，则不会以 `object` 作为 **基类**\n",
    "\n",
    "> **新式类** 和 **经典类** 在多继承时 —— **会影响到方法的搜索顺序**\n",
    "\n",
    "为了保证编写的代码能够同时在 `Python 2.x` 和 `Python 3.x` 运行！\n",
    "今后在定义类时，**如果没有父类，建议统一继承自 `object`**\n",
    "\n",
    "```python\n",
    "class 类名(object):\n",
    "    pass\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 类属性和类方法\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 类的结构\n",
    "* 类属性和实例属性\n",
    "* 类方法和静态方法\n",
    "\n",
    "## 01. 类的结构\n",
    "\n",
    "### 1.1 术语 —— 实例\n",
    "\n",
    "1. 使用面相对象开发，**第 1 步** 是设计 **类**\n",
    "2. 使用 **类名()** 创建对象，**创建对象** 的动作有两步：\n",
    "    * 1) 在内存中为对象 **分配空间**\n",
    "    * 2) 调用初始化方法 `__init__` 为 **对象初始化**\n",
    "3. 对象创建后，**内存** 中就有了一个对象的 **实实在在** 的存在 —— **实例**\n",
    "\n",
    "![017_类的结构示意图I-w473](./res/017_%E7%B1%BB%E7%9A%84%E7%BB%93%E6%9E%84%E7%A4%BA%E6%84%8F%E5%9B%BEI.png)\n",
    "\n",
    "因此，通常也会把：\n",
    "\n",
    "1. 创建出来的 **对象** 叫做 **类** 的 **实例**\n",
    "2. 创建对象的 **动作** 叫做 **实例化**\n",
    "3. **对象的属性** 叫做 **实例属性**\n",
    "4. **对象调用的方法** 叫做 **实例方法**\n",
    "\n",
    "在程序执行时：\n",
    "\n",
    "1. 对象各自拥有自己的 **实例属性**\n",
    "2. 调用对象方法，可以通过 `self.` \n",
    "    * 访问自己的属性\n",
    "    * 调用自己的方法\n",
    "\n",
    "**结论**\n",
    "\n",
    "* **每一个对象** 都有自己 **独立的内存空间**，**保存各自不同的属性**\n",
    "* **多个对象的方法**，**在内存中只有一份**，在调用方法时，**需要把对象的引用** 传递到方法内部\n",
    "\n",
    "### 1.2 类是一个特殊的对象\n",
    "\n",
    "> `Python` 中 **一切皆对象**：\n",
    "> \n",
    "> * `class AAA:` 定义的类属于 **类对象**\n",
    "> * `obj1 = AAA()` 属于 **实例对象**\n",
    "\n",
    "* 在程序运行时，**类** 同样 **会被加载到内存**\n",
    "* 在 `Python` 中，**类** 是一个特殊的对象 —— **类对象**\n",
    "* 在程序运行时，**类对象** 在内存中 **只有一份**，使用 **一个类** 可以创建出 **很多个对象实例**\n",
    "* 除了封装 **实例** 的 **属性** 和 **方法**外，**类对象** 还可以拥有自己的 **属性** 和 **方法**\n",
    "    1. **类属性**\n",
    "    2. **类方法**\n",
    "* 通过 **类名.** 的方式可以 **访问类的属性** 或者 **调用类的方法**\n",
    "\n",
    "![017_类的结构示意图II-w545](./res/017_%E7%B1%BB%E7%9A%84%E7%BB%93%E6%9E%84%E7%A4%BA%E6%84%8F%E5%9B%BEII.png)\n",
    "\n",
    "## 02. 类属性和实例属性\n",
    "\n",
    "### 2.1 概念和使用\n",
    "\n",
    "* **类属性** 就是给 **类对象** 中定义的 **属性**\n",
    "* 通常用来记录 **与这个类相关** 的特征\n",
    "* **类属性** **不会用于**记录 **具体对象的特征**\n",
    "\n",
    "**示例需求**\n",
    "\n",
    "* 定义一个 **工具类**\n",
    "* 每件工具都有自己的 `name`\n",
    "* **需求** —— 知道使用这个类，创建了多少个工具对象？\n",
    "\n",
    "![018_类属性案例I-w263](./res/018_%E7%B1%BB%E5%B1%9E%E6%80%A7%E6%A1%88%E4%BE%8BI.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "现在创建了 3 个工具\n"
     ]
    }
   ],
   "source": [
    "class Tool(object):\n",
    "\n",
    "    # 使用赋值语句，定义类属性，记录创建工具对象的总数\n",
    "    count = 0\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "        # 针对类属性做一个计数+1\n",
    "        Tool.count += 1\n",
    "\n",
    "\n",
    "# 创建工具对象\n",
    "tool1 = Tool(\"斧头\")\n",
    "tool2 = Tool(\"榔头\")\n",
    "tool3 = Tool(\"铁锹\")\n",
    "\n",
    "# 知道使用 Tool 类到底创建了多少个对象?\n",
    "print(\"现在创建了 %d 个工具\" % Tool.count)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 属性的获取机制（科普）\n",
    "\n",
    "* 在 `Python` 中 **属性的获取** 存在一个 **向上查找机制**\n",
    "\n",
    "![019_通过对象访问类属性-w779](./res/019_%E9%80%9A%E8%BF%87%E5%AF%B9%E8%B1%A1%E8%AE%BF%E9%97%AE%E7%B1%BB%E5%B1%9E%E6%80%A7.png)\n",
    "\n",
    "* 因此，要访问类属性有两种方式：\n",
    "    1. **类名.类属性**\n",
    "    2. **对象.类属性** （不推荐）\n",
    "\n",
    "**注意**\n",
    "\n",
    "* 如果使用 `对象.类属性 = 值` 赋值语句，只会 **给对象添加一个属性**，而不会影响到 **类属性的值**\n",
    "\n",
    "## 03. 类方法和静态方法\n",
    "\n",
    "### 3.1 类方法\n",
    "\n",
    "* **类属性** 就是针对 **类对象** 定义的属性\n",
    "    * 使用 **赋值语句** 在 `class` 关键字下方可以定义 **类属性**\n",
    "    * **类属性** 用于记录 **与这个类相关** 的特征\n",
    "* **类方法** 就是针对 **类对象** 定义的方法\n",
    "    * 在 **类方法** 内部可以直接访问 **类属性** 或者调用其他的 **类方法**\n",
    "\n",
    "**语法如下**\n",
    "\n",
    "```python\n",
    "@classmethod\n",
    "def 类方法名(cls):\n",
    "    pass\n",
    "```\n",
    "\n",
    "* 类方法需要用 **修饰器** `@classmethod` 来标识，**告诉解释器这是一个类方法**\n",
    "* 类方法的 **第一个参数** 应该是 `cls`\n",
    "    * 由 **哪一个类** 调用的方法，方法内的 `cls` 就是 **哪一个类的引用**\n",
    "    * 这个参数和 **实例方法** 的第一个参数是 `self` 类似\n",
    "    * **提示** 使用其他名称也可以，不过习惯使用 `cls`\n",
    "3. 通过 **类名.** 调用 **类方法**，**调用方法时**，不需要传递 `cls` 参数\n",
    "4. **在方法内部**\n",
    "    * 可以通过 `cls.` **访问类的属性**\n",
    "    * 也可以通过 `cls.` **调用其他的类方法**\n",
    "\n",
    "**示例需求**\n",
    "\n",
    "* 定义一个 **工具类**\n",
    "* 每件工具都有自己的 `name`\n",
    "* **需求** —— 在 **类** 封装一个 `show_tool_count` 的类方法，输出使用当前这个类，创建的对象个数\n",
    "\n",
    "![020_类方法案例-w263](./res/020_%E7%B1%BB%E6%96%B9%E6%B3%95%E6%A1%88%E4%BE%8B.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "@classmethod\n",
    "def show_tool_count(cls):\n",
    "    \"\"\"显示工具对象的总数\"\"\"\n",
    "    print(\"工具对象的总数 %d\" % cls.count)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 在类方法内部，可以直接使用 `cls` 访问 **类属性** 或者 **调用类方法**\n",
    "\n",
    "### 3.2 静态方法\n",
    "\n",
    "* 在开发时，如果需要在 **类** 中封装一个方法，这个方法：\n",
    "    * 既 **不需要** 访问 **实例属性** 或者调用 **实例方法**\n",
    "    * 也 **不需要** 访问 **类属性** 或者调用 **类方法**\n",
    "    \n",
    "* 这个时候，可以把这个方法封装成一个 **静态方法**\n",
    "\n",
    "**语法如下**\n",
    "\n",
    "```python\n",
    "@staticmethod\n",
    "def 静态方法名():\n",
    "    pass\n",
    "```\n",
    "\n",
    "* **静态方法** 需要用 **修饰器** `@staticmethod` 来标识，**告诉解释器这是一个静态方法**\n",
    "* 通过 **类名.** 调用 **静态方法**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(object):\n",
    "    \n",
    "    # 狗对象计数\n",
    "    dog_count = 0\n",
    "    \n",
    "    @staticmethod\n",
    "    def run():\n",
    "        \n",
    "        # 不需要访问实例属性也不需要访问类属性的方法\n",
    "        print(\"狗在跑...\")\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 方法综合案例\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. 设计一个 `Game` 类\n",
    "2. 属性：\n",
    "    * 定义一个 **类属性** `top_score` 记录游戏的 **历史最高分**\n",
    "    * 定义一个 **实例属性** `player_name` 记录 **当前游戏的玩家姓名**\n",
    "3. 方法：\n",
    "    * **静态方法** `show_help` 显示游戏帮助信息\n",
    "    * **类方法** `show_top_score` 显示历史最高分\n",
    "    * **实例方法** `start_game` 开始当前玩家的游戏\n",
    "4. 主程序步骤\n",
    "    * 1) 查看帮助信息\n",
    "    * 2) 查看历史最高分\n",
    "    * 3) 创建游戏对象，开始游戏\n",
    "\n",
    "![021_方法综合案例-w351](./res/021_%E6%96%B9%E6%B3%95%E7%BB%BC%E5%90%88%E6%A1%88%E4%BE%8B.png)\n",
    "\n",
    "#### 案例小结\n",
    "\n",
    "1. **实例方法** —— 方法内部需要访问 **实例属性**\n",
    "    * **实例方法** 内部可以使用 **类名.** 访问类属性\n",
    "2. **类方法** —— 方法内部 **只** 需要访问 **类属性**\n",
    "3. **静态方法** —— 方法内部，不需要访问 **实例属性** 和 **类属性**\n",
    "\n",
    "**提问**\n",
    "\n",
    "如果方法内部 即需要访问 **实例属性**，又需要访问 **类属性**，应该定义成什么方法？\n",
    "\n",
    "**答案**\n",
    "\n",
    "* 应该定义 **实例方法**\n",
    "* 因为，**类只有一个**，在 **实例方法** 内部可以使用 **类名.** 访问类属性\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "帮助信息：让僵尸走进房间\n",
      "游戏最高分是 0\n",
      "[小明] 开始游戏...\n",
      "游戏最高分是 999\n"
     ]
    }
   ],
   "source": [
    "class Game(object):\n",
    "\n",
    "    # 游戏最高分，类属性\n",
    "    top_score = 0\n",
    "\n",
    "    @staticmethod\n",
    "    def show_help():\n",
    "        print(\"帮助信息：让僵尸走进房间\")\n",
    "        \n",
    "    @classmethod\n",
    "    def show_top_score(cls):\n",
    "        print(\"游戏最高分是 %d\" % cls.top_score)\n",
    "\n",
    "    def __init__(self, player_name):\n",
    "        self.player_name = player_name\n",
    "\n",
    "    def start_game(self):\n",
    "        print(\"[%s] 开始游戏...\" % self.player_name)\n",
    "        \n",
    "        # 使用类名.修改历史最高分\n",
    "        Game.top_score = 999\n",
    "\n",
    "# 1. 查看游戏帮助\n",
    "Game.show_help()\n",
    "\n",
    "# 2. 查看游戏最高分\n",
    "Game.show_top_score()\n",
    "\n",
    "# 3. 创建游戏对象，开始游戏\n",
    "game = Game(\"小明\")\n",
    "\n",
    "game.start_game()\n",
    "\n",
    "# 4. 游戏结束，查看游戏最高分\n",
    "Game.show_top_score()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
